<?php

namespace App\Services\Bolai;

use Exception;

class BKV
{
    private array $_kvs = [];

    public function pack(): array
    {
        if (count($this->_kvs) === 0) {
            return [];
        }

        $buffer = [];
        foreach ($this->_kvs as $kv) {
            $buffer = array_merge($buffer, $kv->pack());
        }

        return $buffer;
    }

    public static function unpack(array $buffer): array
    {
        $bkv = new BKV();
        while (true) {
            $pr = KV::unpack($buffer);
            if ($pr['code'] === 0) {
                if ($pr['kv'] != null) {
                    $bkv->add($pr['kv']);
                }
                $buffer = $pr['pendingParseBuffer'];
            } else {
                if ($pr['code'] === 1) {
                    break;
                } else {
                    return ['code' => $pr['code'], 'bkv' => null, 'pendingParseBuffer' => $pr['pendingParseBuffer']];
                }
            }
        }
        return ['code' => 0, 'bkv' => $bkv, 'pendingParseBuffer' => null];
    }

    public function items(): array
    {
        return $this->_kvs;
    }

    public function add(KV $kv): void
    {
        $this->_kvs[] = $kv;
    }

    public function addByStringKey(string $key, mixed $value, ?int $byteLength = null): void
    {
        $this->add(new KV($key, $value, $byteLength));
    }



    public function addByNumberKey(int $key, mixed $value, ?int $byteLength = null): void
    {
        $this->add(new KV($key, $value, $byteLength));
    }

    public function getStringValue(string $key): ?string
    {
        foreach ($this->_kvs as $kv) {
            if ($kv->key() === $key) {
                return $kv->stringValue();
            }
        }
        return null;
    }

    public function getNumberValue(int $key): ?int
    {
        foreach ($this->_kvs as $kv) {
            if ($kv->key() === $key) {
                return $kv->numberValue();
            }
        }
        return null;
    }

    public function dump(): array
    {

        $result = [];
//        foreach ($this->_kvs as $i => $kv) {
//            $valueString = $this->bufferToHex($kv->value());
//            $valueFirstByte = $kv->value()[0];
//            if ($kv->isStringKey() && 0x20 <= $valueFirstByte && $valueFirstByte <= 0x7E) {
//                $valueString .= " (s: " . $kv->stringValue() . ")";
//            }
//            $val = [
//                'type' => $kv->isStringKey() ? 'string' : 'number',
//                'key' => $kv->key(),
//                'valueCount' => count($kv->value()),
//                'value' => $kv->isStringKey() ? $valueString : '0x' . $valueString, // 数字类型值以十六进制形式输出
//            ];
//            $result[$kv->key()] = $val;
//        }


        foreach ($this->_kvs as $i => $kv) {
            $value = $kv->value();
            if (empty($value)) {
                continue; // 跳过空值
            }
            $valueString = $this->bufferToHex($kv->value());
            $valueFirstByte = $kv->value()[0];
            if (0x20 <= $valueFirstByte && $valueFirstByte <= 0x7E) {
//                $valueString .= " (s: " . $kv->stringValue() . ")";
                //               $valueString .= $kv->stringValue();
            }
            $val = [];
            if ($kv->isStringKey()) {
                $val['type'] = 'string';
            } else {
                $val['type'] = 'number';
            }
            $val['key'] = $kv->key();
            $val['valueCount'] = count($kv->value());
            $val['value'] = $valueString;
            $result[$kv->key()] = $val;
        }
        return $result;
    }

    public function bufferToHex(array $buffer): string
    {
        $hex = '';
        foreach ($buffer as $b) {
            $hex .= sprintf("%02x", $b);
        }
        return $hex;
    }


    public function concatenateBuffer(array ...$arrays): array
    {
        $result = [];
        foreach ($arrays as $arr) {
            $result = array_merge($result, $arr);
        }
        return $result;
    }


    public function hexToBuffer(string $hex): array
    {
        if (strlen($hex) % 2 !== 0) {
            throw new Exception("Hex string length must be even");
        }
        $buffer = [];
        for ($i = 0; $i < strlen($hex); $i += 2) {
            $buffer[] = hexdec(substr($hex, $i, 2));
        }
        return $buffer;
    }


    public function toBcd(int $number): string {
        // 计算十位数和个位数
        $tens = intval($number / 10);   // 十位数
        $units = $number % 10;          // 个位数

        // 将十位数和个位数组合为 BCD 编码
        $bcd = ($tens << 4) | $units;

        // 返回 BCD 编码的字符
        return chr($bcd);
    }

    public function getBcdTime(): string {
        // 获取当前时间
        $year = date('Y'); // 4 位年份，例如 2023
        $month = date('m'); // 月份，01-12
        $day = date('d');   // 日期，01-31
        $hour = date('H');  // 小时，00-23
        $minute = date('i'); // 分钟，00-59
        $second = date('s'); // 秒数，00-59

        // 将年份拆分为高 2 位和低 2 位
        $yearHigh = intval(substr($year, 0, 2)); // 20
        $yearLow = intval(substr($year, 2, 2));  // 23

        // 转换为 BCD 格式
        $bcdYearHigh = $this->toBcd($yearHigh); // 0x20
        $bcdYearLow = $this->toBcd($yearLow);   // 0x23
        $bcdMonth = $this->toBcd($month);       // 0x01 - 0x12
        $bcdDay = $this->toBcd($day);           // 0x01 - 0x31
        $bcdHour = $this->toBcd($hour);         // 0x00 - 0x23
        $bcdMinute = $this->toBcd($minute);     // 0x00 - 0x59
        $bcdSecond = $this->toBcd($second);     // 0x00 - 0x59

        // 拼接为 7 字节的 BCD 时间
        return $bcdYearHigh . $bcdYearLow . $bcdMonth . $bcdDay . $bcdHour . $bcdMinute . $bcdSecond;
    }


}

